package org.zw.android.framework.http;

import java.util.ArrayList;
import java.util.List;

import org.zw.android.framework.async.AsyncTaskExecutor;
import org.zw.android.framework.async.AsyncTaskRunnable;
import org.zw.android.framework.http.ResponseBean.Status;
import org.zw.android.framework.util.StringUtils;

import android.util.Log;

/**
 * Http Request Executor
 * 
 * @author zhouwei
 *
 */
public final class HttpFactory {
	
	private enum HttpMethod {
		GET , POST , PUT , DELETE
	}
	
	static final String TAG = "HttpService" ;
	
	// Global filters
	private static final List<FilterResponse> mFilters = new ArrayList<FilterResponse>();
	
	// HTTP Executor (Apache HTTP)
	private static HttpExecutor 	mExecutor ;
	
	static {
		mExecutor = new HttpExecutorApache() ;
	}
	
	public static void setHttpExecutor(HttpExecutor executor){
		mExecutor	= executor ;
	}
	
	/**
	 * clear Global filters 
	 */
	public static void clearFilters(){
		mFilters.clear() ;
	}
	
	/**
	 * Add Global Filter ; for ex : ErrorFilter , NormalFilter
	 * 
	 * @param filter
	 */
	public static void addFilter(FilterResponse filter){
		
		if(filter == null) return ;
		
		if(!mFilters.contains(filter)){
			mFilters.add(filter);
		}
	}
	
	public static void downloadFile(String url, final DownloadCallback callback,final HttpUIHandlerDownload handler){
		
		// If the request is null
		if (StringUtils.isEmpty(url))
			return;
		
		if(callback == null){
			Log.e(TAG, "zhouwei >> Download file callback is null");
			return ;
		}
		
		final HttpRequest req = new HttpRequest(url);
		
		AsyncTaskExecutor.execute(new AsyncTaskRunnable(handler) {

			@Override
			public Object onProcessing() {
				
				try{
					
					ResponseBean input = mExecutor.downloadFile(req);
					
					if(input != null){
						
						Object res = callback.download(req,input,handler);
						
						if(handler != null){
							
							if(res != null){
								Log.d(TAG, "zhouwei >> file download finish");
								handler.sendDownloadFinish(res) ;
							} else {
								handler.sendErrorMessage("Callback return is null");
							}
							
							return res ;
						}
						
						Log.w(TAG, "zhouwei >> file download failed");
						
					} else {
						// send Exception to UI
						notifyException(new NullPointerException(req.getUrl() + " response is null"));
					}
					
				} catch(Exception e){
					e.printStackTrace() ;
					
					// when exception
					ResponseBean bean = new ResponseBean() ;
					bean.setStatus(Status.FAILED);
					bean.setResponse(e);
					doExceptionFilter(req,bean,handler);
					
					// send Exception to UI
					notifyException(e);
				}
				
				return null;
			}
			
		});
	}
	
	/**
	 * get
	 * @param request
	 * @param parser
	 * @param handler
	 */
	public static void doGet(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler){
		doHttp(HttpMethod.GET, request, parser, handler) ;
	}
	
	/**
	 * post
	 * @param request
	 * @param parser
	 * @param handler
	 */
	public static void doPost(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler){
		doHttp(HttpMethod.POST, request, parser, handler) ;
	}
	
	/**
	 * put
	 * @param request
	 * @param parser
	 * @param handler
	 */
	public static void doPut(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler){
		doHttp(HttpMethod.PUT, request, parser, handler) ;
	}
	
	/**
	 * delete
	 * 
	 * @param request
	 * @param parser
	 * @param handler
	 */
	public static void doDelete(final HttpRequest request,final HttpWrapper parser,HttpUIHandler handler){
		doHttp(HttpMethod.DELETE, request, parser, handler) ;
	}
	
	// do http
	private static void doHttp(final HttpMethod method ,
			final HttpRequest request,
			final HttpWrapper parser,
			final HttpUIHandler handler){
		
		// If the request is null
		if(request == null) return ;
		
		AsyncTaskExecutor.execute(new AsyncTaskRunnable(handler) {
			
			@Override
			public Object onProcessing() {
				
				// Execute HTTP
				try {
					
					long enter = System.currentTimeMillis() ;
					
					ResponseBean bean = null ;
					
					if(method == HttpMethod.GET){
						bean = mExecutor.doGet(request);
					} else if(method == HttpMethod.POST){
						bean = mExecutor.doPost(request);
					} else if(method == HttpMethod.DELETE){
						bean = mExecutor.doDelete(request);
					} else if(method == HttpMethod.PUT){
						bean = mExecutor.doPut(request);
					}
					
					Log.d(TAG, "zhouwei >> Server Response time = " + (System.currentTimeMillis() - enter));
					
					// filter normal
					if(!doNormalFilter(request,bean,handler)){
						
						Log.w(TAG, "zhouwei >> filter process failed");
						
						if(handler != null){
							handler.sendNormalFilterFailed(bean != null ? bean.getResponse() : null);
						}
						
						return null ;
					}
					
					Object obj = bean != null ? bean.getResponse() : null ;
					
					// if parser filter is null
					if(parser == null){
						
						Log.w(TAG, "zhouwei >> http request parser filter is null");
						
						if(handler != null){
							handler.sendParserFilterFailed("parser filter is null");
						}
						
						return obj ;
					}
					
					enter = System.currentTimeMillis() ;
					
					// parser Filter
					Object res = parser.wrapper(obj) ;
					
					Log.d(TAG, "zhouwei >> FilterParser time = " + (System.currentTimeMillis() - enter));
					
					return res ;
				} catch(Exception e) {
					e.printStackTrace() ;
					
					// when exception
					ResponseBean bean = new ResponseBean() ;
					bean.setStatus(Status.FAILED);
					bean.setResponse(e);
					doExceptionFilter(request,bean,handler);
					
					// send Exception to UI
					notifyException(e);
				}
				
				return null;
			}
		}) ;
	}
	
	// Normal
	private static boolean doNormalFilter(HttpRequest request,ResponseBean bean,HttpUIHandler handler){
		
		final List<FilterResponse> temp = mFilters ;
		
		// filter normal
		for(FilterResponse f : temp){
			
			// if filter return true, the process stop and process next
			if(f instanceof FilterNormal){
				if(f.doFilter(request, bean,handler)){
					return true ;
				}
			}
		}
		
		return false ;
	}
	
	// Exception
	private static void doExceptionFilter(HttpRequest request,ResponseBean bean,HttpUIHandler handler){
		
		final List<FilterResponse> temp = mFilters ;
		
		// filter exception
		for(FilterResponse f : temp){
			
			// if filter return true, the process stop
			if(f instanceof FilterException){
				if(f.doFilter(request, bean,handler)) return;
			}
		}
	}
}
